<?php
defined('BASEPATH') OR exit('No direct script access allowed');

/**
 * 通行证函数库
 * 
 * @desc 
 * @version 1.0.0 
 * @author  Jason
 * @date    2017年4月25日 下午3:36:25
 */
class Lib_passport
{
	/**
	 * Current CI instance object
	 * 
	 * @desc CI_Controller::get_instance();
	 * @var CI_Controller $CI
	 */
	private $CI;
	
	/**
	 * CI Encryption
	 * 
	 * @var CI_Encryption $encryption
	 */
	private $encryption = null;
	
	/** @var Mod_member */
	private $mod_member = null;
	
	public function __construct()
	{
		$this->CI =& get_instance();
		
		$this->CI->load->library('encryption');
		$this->encryption =& $this->CI->encryption;
		
		// 这里加载数据库是为了验证表单提交时的查库部分，比如 is_unique 
		// 不过，如果设置了自动加载DB，或者构造里面加载了模型，模型构造里面加载了DB，都是可以省略的！
		$this->CI->load->database(); // for is_unique
		
		$this->CI->load->model('mod_member');
		$this->mod_member =& $this->CI->mod_member;
		
	}
	
	public function get_token_by_member($member)
	{
		$tokenData = [
				'id' => $member['id'],
				'ps' => $member['password'],
				'ct' => time(),
		];
		# 密钥是 16 进制字符串格式，16，24，32 字节。
		$keyStream = $member['password'];
		$key = substr($keyStream, strrpos($keyStream, '$') + 1, 16);
		$this->encryption->initialize(['key' => $key]);
		$dataString = serialize($tokenData);
		//$cipherBase64 = base64_encode($tokenBin);
		$cipherBase64 = $this->encryption->encrypt($dataString);
		// 两个方法在遇到错误时都会返回 FALSE ，
		// 如果是 encrypt() 返回 FALSE ， 那么只可能是配置参数错了。
		assert($cipherBase64 !== FALSE, 'Check encryption Config, Please!');
		$tokenString = "{$member['id']}|{$cipherBase64}";
		// var_dump($dataString, $tokenString, base64_encode($tokenString));
		// Base64_encode 一次长度增大 1/3, 少用！
		
		// $tokenString = urlencode($tokenString);
		# $tokenString = str_replace('+', '.', $tokenString);
		$tokenString = str_replace(['+', '/'], ['-', '_'], $tokenString);
		
		return $tokenString;
	}
	
	public function get_member_by_token($tokenString)
	{
		//$tokenString = urldecode($tokenString);
		# $tokenString = str_replace('.', '+', $tokenString);
		$tokenString = str_replace(['-', '_'], ['+', '/'], $tokenString);
		
		
		@list($memberId, $cipherBase64) = explode('|', $tokenString);
		$member = $this->mod_member->get_member_by_id($memberId);
		if (empty($member['password'])) {
			return [];
		}
		# 密钥是 16 进制字符串格式，16，24，32 字节。
		$keyStream = $member['password'];
		$key = substr($keyStream, strrpos($keyStream, '$') + 1, 16);
		$this->encryption->initialize(['key' => $key]);
		$dataString = $this->encryption->decrypt($cipherBase64);
		// 在生产代码中一定要对 decrypt() 方法进行检查。
		if ($dataString === FALSE) {
			return [];
		}
		$tokenData = @unserialize($dataString);
		
		if (empty($tokenData['ps']) || $tokenData['ps'] != $keyStream) {
			return [];
		}
		
		return $member;
	}
	
	// MARK validation method: is_not_unique
	// MARK 这里通过修改 CI-Libraries-Form_validation Callable 也可带第二个参数，仅限字符串
	function is_not_unique($str, $field)
	{
		//var_dump($str, $field);exit;
		return !$this->CI->form_validation->is_unique($str, $field);
	}
	
	function is_token_valid($token)
	{
		$member = $this->get_member_by_token($token);
		if (empty($member)) {
			return false;
		}
		
		$this->CI->session->set_userdata('token_member', $member);
		return true;
	}
	
	function is_password_valid($password)
	{
		$member = $this->CI->get_logined_member();
		$password_hash = $this->mod_member->select_fields_by_id($member['id'], 'password');
		$verify_result = $this->mod_member->verify_member_password($password, $password_hash);
		if (!$verify_result) {
			return false;
		}
		
		$this->CI->session->set_userdata('session_member', $member);
		return true;
	}
	
	/**
	 * 前台用户注册处理器
	 * 
	 * @method  POST
	 * @author  Jason
	 * @date    2017年4月25日 下午6:44:21
	 * @param   $name, $password, $passconf, $email, $agreement
	 * 
	 * @return  [<status>, <content>]	一个两元素数组，第一个元素表示状态，第二个表示相应内容
	 * 	        eg:	[true, <userData>]
	 * 				[false, <errorMesg>]
	 */
	public function register()
	{
		$this->CI->load->library('form_validation');
		// var_dump($this->CI->db->get('member'));
		$this->CI->load->database(); // for is_unique
		
		$status = false;
		$content = [];
		if(FALSE === $this->CI->form_validation->run('register')) {
			$errorKey = $this->CI->form_validation->error_array();
			$status = false;
			$content = $errorKey;
		} else {
			$data = $this->CI->input->post();
			$member = $this->mod_member->register_member($data);
			if (!empty($member['id'])) {
				$status = true;
				$content = $member;
			} else {
				// passport_msg_register_faild
				$content = ['error'];
			}
		}
		
		return [$status, $content];
	}
	
	/**
	 * 前台用户登录处理器
	 *
	 * @method  POST
	 * @author  Jason
	 * @date	2017年4月26日 下午3:55:29
	 * @param   $name, $password
	 *
	 * @return  [<status>, <content>]	一个两元素数组，第一个元素表示状态，第二个表示相应内容
	 * 	        eg:	[true, <userData>]
	 * 				[false, <errorMesg>]
	 */
	public function login()
	{
		$this->CI->load->library('form_validation');
		
		$status = false;
		$content = ['error'];
		if(FALSE === $this->CI->form_validation->run('login')) {
			$errorKey = $this->CI->form_validation->error_array();
			$status = false;
			$content = $errorKey;
		} else {
			$data = $this->CI->input->post();
			$name = $data['name'];
			$password = $data['password'];
			$member = $this->mod_member->get_member_by_login($name, $password);
			if (!empty($member['id'])) {
				$status = true;
				$content = $member;
				$status_tags = $this->mod_member->calculate_status_tags($member['status'], 'negative');
				if (in_array('disabled', $status_tags)) {
					$status = false;
					$content = ['passport_msg_member_status_disabled'];
				}
			} else {
				// passport_msg_login_faild
				$content = [];
			}
		}
		
		return [$status, $content];
	}
	
	/**
	 * 管理员登录处理器
	 *
	 * @method  POST
	 * @author  Jason
	 * @date	2017年5月11日 下午5:12:38
	 * @param   $name, $password
	 *
	 * @return  [<status>, <content>]	一个两元素数组，第一个元素表示状态，第二个表示相应内容
	 * 	        eg:	[true, <userData>]
	 * 				[false, <errorMesg>]
	 */
	public function manager_login()
	{
		$this->CI->load->library('form_validation');
		
		$status = false;
		$content = ['error'];
		if(FALSE === $this->CI->form_validation->run('login')) {
			$errorKey = $this->CI->form_validation->error_array();
			$status = false;
			$content = $errorKey;
		} else {
			$data = $this->CI->input->post();
			$name = $data['name'];
			$password = $data['password'];
			$member = $this->mod_member->get_member_by_login($name, $password);
			if (!empty($member['id'])) {
				$status = true;
				$content = $member;
				$this->CI->config->load('dbconst');
				$merchant_role_id = $this->CI->config->item('manager', 'role_id');
				$status_tags = $this->mod_member->calculate_status_tags($member['status'], 'negative');
				if (in_array('disabled', $status_tags)) {
					$status = false;
					$content = ['passport_msg_member_status_disabled'];
				} else if (empty($member['role_id']) || $member['role_id'] !== $merchant_role_id) {
					$status = false;
					$content = ['passport_msg_member_role_not_manager'];
				}
			} else {
				// passport_msg_login_faild
				$content = [];
			}
		}
		
		return [$status, $content];
	}
	
	public function login_remember_me($expires)
	{
// 		print_r($ci->session);
// 		var_dump($ci->config->item('sess_cookie_name'));

		$session_id = $this->CI->session->session_id;
		$expiration = $expires;
		$session_config = [];
		$session_config['cookie_name'] = config_item('sess_cookie_name');
		$session_config['cookie_lifetime'] = time() + $expiration;
		$session_config['cookie_path'] = config_item('cookie_path');
		$session_config['cookie_domain'] = config_item('cookie_domain');
		$session_config['cookie_secure'] = config_item('cookie_secure');
		$http_only = TRUE;
		
		// $this->CI->load->library('lib_cookie');
		setcookie(
				$name = $session_config['cookie_name'], 
				$value = $session_id, 
				$expire = $session_config['cookie_lifetime'], 
				$path = $session_config['cookie_path'], 
				$domain = $session_config['cookie_domain'], 
				$secure = $session_config['cookie_secure'], 
				$httponly = $http_only
		);
	}
	
	/**
	 * 修改密码 - 修改登录者本人的密码
	 *
	 * @method  POST
	 * @author  Jason
	 * @date	2017年6月16日 下午5:46:05
	 * @param   $opassword, $password, $passconf
	 *
	 * @return  [<status>, <content>]	一个两元素数组，第一个元素表示状态，第二个表示相应内容
	 * 	        eg:	[true, <userData>]
	 * 				[false, <errorMesg>]
	 */
	public function change_password()
	{
		$this->CI->load->library('form_validation');
		
		$status = false;
		$content = [];
		if(FALSE === $this->CI->form_validation->run('change_password')) {
			$errorKey = $this->CI->form_validation->error_array();
			$status = false;
			$content = $errorKey;
		} else {
			$data = $this->CI->input->post();
			$password = $data['password'];
			// 从 callback: is_password_valid() 函数传过来的 member 数据
			$member = $this->CI->session->get_userdata('session_member')['session_member'];
			$this->CI->session->unset_userdata('session_member');
			
			$status = $this->mod_member->update_member_password($member['id'], $password);
		}
		
		return [$status, $content];
	}
	
	/**
	 * 找回密码 - 确认邮箱
	 *
	 * @method  POST
	 * @author  Jason
	 * @date    2017年4月27日 下午5:15:05
	 * @param   $email
	 *
	 * @return  [<status>, <content>]	一个两元素数组，第一个元素表示状态，第二个表示相应内容
	 * 	        eg:	[true, <userData>]
	 * 				[false, <errorMesg>]
	 */
	public function recovery_submit_eamil()
	{
		$this->CI->load->library('form_validation');
		
		##$this->CI->config->load('form_validation', !true);
		##$errors = $this->CI->config->item('errors', 'common');
		
		##$errors['is_not_unique'] = "Email not found!";
		#$this->CI->form_validation->set_message('is_not_unique', 'Email not found!');
		
		#$config = [
		#		[
		#				'field' => 'email',
		#				'label' => 'email',
		#				'rules' => 'trim|required|valid_email|callback_is_not_unique[member.email]',
		#				'errors' => $errors,
		#		],
		#];
		#$this->CI->form_validation->set_rules($config);
		
		$status = false;
		$content = [];
		if(FALSE === $this->CI->form_validation->run('recovery_submit_eamil')) {
			$errorKey = $this->CI->form_validation->error_array();
			$status = false;
			$content = $errorKey;
		} else {
			$data = $this->CI->input->post();
			$email = $data['email'];
			
			$url = $this->get_security_url_by_email($email);
			
			$status = true;
			$content = [
					'email' => $email,
					'url' => $url,
			];
		}
		
		return [$status, $content];
	}
	
	/**
	 * 找回密码 - 密码重置
	 *
	 * @method  POST
	 * @author  Jason
	 * @date    2017年4月28日 上午11:35:04
	 * @param   $token, $password, $passconf
	 *
	 * @return  [<status>, <content>]	一个两元素数组，第一个元素表示状态，第二个表示相应内容
	 * 	        eg:	[true, <userData>]
	 * 				[false, <errorMesg>]
	 */
	public function recovery_reset()
	{
		$this->CI->load->library('form_validation');
		
		$status = false;
		$content = [];
		if(FALSE === $this->CI->form_validation->run('recovery_reset')) {
			$errorKey = $this->CI->form_validation->error_array();
			$status = false;
			$content = $errorKey;
		} else {
			$data = $this->CI->input->post();
			$password = $data['password'];
			// 从 callback: token_valid() 函数传过来的 member 数据
			$member = $this->CI->session->get_userdata('token_member')['token_member'];
			$this->CI->session->unset_userdata('token_member');
			
			$status = $this->mod_member->update_member_password($member['id'], $password);
		}
		
		return [$status, $content];
	}
	
	
	private function get_security_url_by_email($email)
	{
		$member = $this->mod_member->get_member_by_email($email);
		if (empty($member)) {
			return null;
		}
		
		$token = $this->get_token_by_member($member);
		$this->CI->load->helper('url');
		// The URI you submitted has disallowed characters. ( | or = )
		// $token = urlencode(base64_encode($token));
		// $security_url = site_url("passport/recovery_reset/{$token}");
		$security_url = site_url("passport/recovery_reset/") . "?token={$token}";
		
		return $security_url;
	}
}